﻿
using System.Collections.Generic;

using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;



namespace RayDen.RayEngine.Engines.ProgressivePhotonMapping
{
    public class PPMFilm : ImageFilm<PPMEyePath>
    {
        internal PPMEngineContext engineContext;
        public PPMFilm(int width, int height) : base(width, height)
        {
            SplatMode = true;
        }

        public override void Consume(PathSamplerBase pt)
        {
            var path = (PPMEyePath)pt;
            //SampleHitPoint(Samples, ,);
            this.Samples.SplatSample(path.Sample, path.Radiance, 1.0f);
            // Check if the sample buffer is full
            if (this.Samples.IsFull())
            {
                this.statsTotalSampleCount += this.Samples.GetSampleCount();

                // Splat all samples on the film
                this.SplatSampleBuffer(this.IsPreviewOver(), this.Samples);
                this.Samples = this.GetFreeSampleBuffer();
            }
        }

        public void SplatHitPoints(List<PPMHitPoint> hitPoints, PPMContext engine)
        {
            var sb = GetFreeSampleBuffer();
            for (int index = 0; index < hitPoints.Count; index++)
            {
                var ppmHitPoint = hitPoints[index];
                sb.SampleHitPoint(ppmHitPoint, engine.PhotonMapPhotons);

                if (sb.IsFull())
                {
                    SplatSampleBuffer(true, sb);
                    FreeSampleBuffer(sb);
                    sb = GetFreeSampleBuffer();
                }
            }
        }
        public void SplatHitPoints()
        {
            var sb = GetFreeSampleBuffer();
            for (int index = 0; index < engineContext.hitPoints.Count; index++)
            {
                var ppmHitPoint = engineContext.hitPoints[index];
                sb.SampleHitPoint(ppmHitPoint, engineContext.MaxPoints);

                if (sb.IsFull())
                {
                    SplatSampleBuffer(true, sb);
                    FreeSampleBuffer(sb);
                    sb = GetFreeSampleBuffer();
                }
            }
        }
        protected override void UpdateFrameBuffer()
        {
            this.SplatHitPoints();
            var pixelCount = this.Width * this.Height;
            var sampledPixels = this.pixelData;
            var pix = new RgbSpectrum();
            if (!this.ToneMap)
            {
                for (int i = 0; i < pixelCount; ++i)
                {
                    if (this.SplatMode)
                    {
                        pix = new RgbSpectrum(this.Radiance2PixelFloat(sampledPixels[i].Radiance.c1),
                                              this.Radiance2PixelFloat(sampledPixels[i].Radiance.c2),
                                              this.Radiance2PixelFloat(sampledPixels[i].Radiance.c3));
                        this.SetPixel(i, ref pix);
                    }
                    else
                    {
                        float weight = sampledPixels[i].Weight;
                        if (weight > 0f)
                        {
#if !RAW

                            pix = new RgbSpectrum(
                                             this.Radiance2PixelFloat((sampledPixels[i].Radiance / weight).c1),
                                             this.Radiance2PixelFloat((sampledPixels[i].Radiance / weight).c2),
                                             this.Radiance2PixelFloat((sampledPixels[i].Radiance / weight).c3));

                            pix = new RgbSpectrum((sampledPixels[i].Radiance)).Transform();
                            //s.Apply(Radiance2PixelFloat);
#else
                                                   pix = new RgbSpectrum(Radiance2PixelFloat(sampledPixels[i].radiance.c1),
                                                                         Radiance2PixelFloat(sampledPixels[i].radiance.c2),
                                                                         Radiance2PixelFloat(sampledPixels[i].radiance.c3));
#endif


#if NOGAMMA
                            pix = sampledPixels[i].radiance;
#endif
                            this.SetPixel(i, ref pix);
                        }
                    }
                }
            }
            else if (this.toneMap != null)
            {
                switch (this.toneMap.GetToneMapType())
                {
                    case ToneMapType.TONEMAP_LINEAR:
                        var tmparam = (LinearToneMapParams)this.toneMap;
                        for (var j = 0; j < pixelCount; ++j)
                        {
                            if (this.SplatMode)
                            {
                                pix = new RgbSpectrum(
                                              this.Radiance2PixelFloat(sampledPixels[j].Radiance.c1),
                                              this.Radiance2PixelFloat(sampledPixels[j].Radiance.c2),
                                              this.Radiance2PixelFloat(sampledPixels[j].Radiance.c3));
                                this.SetPixel(j, ref pix);
                            }
                            else
                            {
                                float weight = sampledPixels[j].Weight;

                                if (weight > 0f && !sampledPixels[j].Radiance.IsBlack())
                                {
                                    float invWeight = tmparam.scale / weight;
                                    var s = new RgbSpectrum((sampledPixels[j].Radiance * invWeight)).Transform();
                                    //s.Apply(Radiance2PixelFloat);

#if SPECTRUM
                                    var color = (sampledPixels[i].radiance*invWeight).ToRgb();
                                                                       
                                    frameBuffer.SetPixel(i,
                                                         color.r,
                                                         color.g,
                                                         color.b);
#else

                                    this.SetPixel(j, ref s);
#endif
                                }

                            }
                        }
                        break;
                    case ToneMapType.TONEMAP_REINHARD02:
                        float alpha = .9f;
                        var tm = (Reinhard02ToneMapParams)this.toneMap;
                        float preScale = tm.preScale;
                        float postScale = tm.postScale;
                        float burn = tm.burn;

                        float Ywa = 0f;
                        for (int ii = 0, j = 0; ii < pixelCount; ++ii)
                        {
                            float weight = sampledPixels[ii].Weight;

                            if (weight > 0f)
                            {
                                float invWeight = 1f / weight;

                                RgbSpectrum rgb = sampledPixels[ii].Radiance * invWeight;

                                // Convert to XYZ color space
                                sampledPixels[ii].Radiance.c1 = 0.412453f * rgb.c1 + 0.357580f * rgb.c2 + 0.180423f * rgb.c3;
                                sampledPixels[ii].Radiance.c2 = 0.212671f * rgb.c1 + 0.715160f * rgb.c2 + 0.072169f * rgb.c3;
                                sampledPixels[ii].Radiance.c3 = 0.019334f * rgb.c1 + 0.119193f * rgb.c2 + 0.950227f * rgb.c3;

                                // var xyz = pixelsRadiance[i].XYZ();

                                Ywa += sampledPixels[ii].Radiance.c2;

                            }
                        }
                        Ywa /= pixelCount;

                        // Avoid division by zero
                        if (Ywa == 0f)
                            Ywa = 1f;

                        float Yw = preScale * alpha * burn;
                        float invY2 = 1f / (Yw * Yw);
                        float pScale = postScale * preScale * alpha / Ywa;

                        for (int ii = 0, j = 0; ii < pixelCount; ++ii)
                        {
                            RgbSpectrum xyz = sampledPixels[ii].Radiance;

                            float ys = xyz.c2;
                            xyz *= pScale * (1f + ys * invY2) / (1f + ys);

                            // Convert back to RGB color space
                            sampledPixels[ii].Radiance.c1 = 3.240479f * xyz.c1 - 1.537150f * xyz.c2 - 0.498535f * xyz.c3;
                            sampledPixels[ii].Radiance.c2 = -0.969256f * xyz.c1 + 1.875991f * xyz.c2 + 0.041556f * xyz.c3;
                            sampledPixels[ii].Radiance.c3 = 0.055648f * xyz.c1 - 0.204043f * xyz.c2 + 1.057311f * xyz.c3;


                            this.frameBuffer.SetPixel(ii, this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c1),
                                                     this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c2),
                                                     this.Radiance2PixelFloat(sampledPixels[ii].Radiance.c3));

                        }

                        break;
                }
            }
        }
    }
}
